﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MudEngine.Parsing.AST
{
    public enum AssignmentOperatorType
    {
        Assign = 0,
        Add = 1,
        Subtract = 2,
        Multiply = 3,
        Divide = 4,
        Modulus = 5,
        ShiftLeft = 6,
        ShiftRight = 7,
        BitwiseAnd = 8,
        BitwiseOr = 9,
        ExclusiveOr = 10,
        Any = 11,
    }

    public class AssignmentExpression : Expression
    {
        public const ResultType ALLOWED_TYPES = ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING | ResultType.ARRAY | ResultType.MAP;
        public const ResultType ALLOWED_TYPES_ASSIGN = ResultType.VAR | ResultType.SHORT | ResultType.INT | ResultType.LONG | ResultType.REAL | ResultType.STRING | ResultType.OBJECT | ResultType.ARRAY | ResultType.MAP;

        public AstNode Left { get; private set; }
        public AssignmentOperatorType Operator { get; private set; }
        public Expression Right { get; private set; }

        public AssignmentExpression(AstNode node, AssignmentOperatorType op, Expression expression)
        {
			this.Left = node;
			if (this.Left != null)
				this.Left.Parent = this;
            this.Operator = op;
			this.Right = expression;
			if (this.Right != null)
				this.Right.Parent = this;
        }

		public static string OperatorName(AssignmentOperatorType op)
		{
			switch (op)
			{
				case AssignmentOperatorType.Assign: return "=";
				case AssignmentOperatorType.Add: return "+=";
				case AssignmentOperatorType.Subtract: return "-=";
				case AssignmentOperatorType.Multiply: return "*=";
				case AssignmentOperatorType.Divide: return "/=";
				case AssignmentOperatorType.Modulus: return "%=";
				case AssignmentOperatorType.ShiftLeft: return "<<=";
				case AssignmentOperatorType.ShiftRight: return ">>=";
				case AssignmentOperatorType.BitwiseAnd: return "&=";
				case AssignmentOperatorType.BitwiseOr: return "|=";
				case AssignmentOperatorType.ExclusiveOr: return "^=";
			}
			return "illegal operator";
		}

		public CheckExpressionResult CheckExpressions()
		{
			switch (this.Operator)
			{
				case AssignmentOperatorType.Subtract:
				case AssignmentOperatorType.Multiply:
				case AssignmentOperatorType.Divide:
				case AssignmentOperatorType.Modulus:
				case AssignmentOperatorType.ShiftLeft:
				case AssignmentOperatorType.ShiftRight:
				case AssignmentOperatorType.BitwiseAnd:
				case AssignmentOperatorType.BitwiseOr:
				case AssignmentOperatorType.ExclusiveOr:
					if (!ALLOWED_TYPES.HasFlag(this.Left.Type.SystemType))
						return CheckExpressionResult.LEFT;
					else if (!ALLOWED_TYPES.HasFlag(this.Right.Type.SystemType))
						return CheckExpressionResult.RIGHT;
					else if (!CompatibleTypes(COMPATIBLES, this.Left.Type.SystemType, this.Right.Type.SystemType))
						return CheckExpressionResult.RIGHT | CheckExpressionResult.LEFT;
					break;
				case AssignmentOperatorType.Assign:
					if (!ALLOWED_TYPES_ASSIGN.HasFlag(this.Left.Type.SystemType))
						return CheckExpressionResult.LEFT;
					else if (!COMPATIBLES[GetBit(this.Left.Type.SystemType)].HasFlag(this.Right.Type.SystemType))
						return CheckExpressionResult.RIGHT;
					break;
				case AssignmentOperatorType.Add:
					if (!ALLOWED_TYPES.HasFlag(this.Left.Type.SystemType))
						return CheckExpressionResult.LEFT;
					else if (!ALLOWED_TYPES_ASSIGN.HasFlag(this.Right.Type.SystemType))
						return CheckExpressionResult.RIGHT;
					else if (!CompatibleTypes(COMPATIBLES, this.Left.Type.SystemType, this.Right.Type.SystemType))
						return CheckExpressionResult.RIGHT | CheckExpressionResult.LEFT;
					break;
			}
			return CheckExpressionResult.OK;
		}

        public override void AcceptVisitor<C>(IAstNodeVisitor<C> visitor, C ctx)
        {
			visitor.VisitAssignmentExpression(this, ctx);
        }
    }
}
